package gen.background;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import gen.background.comment.CommentGenerator;
import gen.background.constant.TableColumn;
import gen.background.core.JavaGenerate;
import gen.background.entity.Column;
import gen.background.entity.TableObject;
import gen.background.entity.TemplateObject;
import gen.background.util.ColumnNameSwitch;
import gen.background.util.DataTypeSwitch;
import gen.background.util.DefaultColumnNameSwitch;
import gen.background.util.DefaultDataTypeSwitch;
import gen.background.util.DefaultPathAnalysis;
import gen.background.util.PathAnalysis;
import gen.background.util.codingUtil;

public class TemplateParse {
	
	private String parseSign = "@#";
	private Connection conn;
	private CommentGenerator commentGenerator;
	
	private Map<String,TemplateObject> temps = new HashMap<String,TemplateObject>();
	private List<TableObject> tables = new ArrayList<TableObject>();
	private ColumnNameSwitch columnNameSwitch;
	private DataTypeSwitch dataTypeSwitch = new DefaultDataTypeSwitch();
	private Map<String,String> tempVariables = new HashMap<String, String>();
	private String defaultLocal;
	
	//---------------------------------未定义配置
	private String charset = "UTF-8";
	PathAnalysis pathAnalysis = new DefaultPathAnalysis();
	
	/**
	 * 模板解析
	 * 按模板规则与模板对象解析成指定文件
	 * @param template
	 * @return
	 * @throws Exception 
	 */
	public boolean parse(String configXML) throws Exception{
		//解析xml配置
		parseXML(configXML);
		
		//获取表字段
		for(TableObject table:tables){
			//获取table字段
			PreparedStatement ps = 
					conn.prepareStatement("select * from information_schema.columns where table_name = '{name}'".replace("{name}", table.getTableName()));
			ResultSet rs = ps.executeQuery();
			if(rs == null){
				throw new RuntimeException(table.getTableName()+" 不存在！");
			}
			table.setColumns(disposeColumn(rs));
		}
		
		//生成文件
		generateFile();
		
		return true;
	}
	
	public void generateFile() throws Exception{
		
		for(TableObject table:tables){
			for(TemplateObject tempObj : temps.values()){
				String uri = tempObj.getTargetSRC().replaceAll("\\.", "/")+"/"
						+table.getDomainObjectName()
						+(tempObj.getTargetSuffix()==null?"":tempObj.getTargetSuffix())
						+"."+tempObj.getType();
				JavaGenerate jg = new JavaGenerate();
				//解析组装模板
				assembly(jg, tempObj.getTemplate());
				
				//生成模板
				File file = getFile(
						(tempObj.getLocal() != null && tempObj.getLocal() != ""?tempObj.getLocal():
							defaultLocal != null && defaultLocal != ""?defaultLocal:"" )
						+"/src/main/java/"+uri);
				jg.execute(table, file, tempVariables);//param 
			}
		}
	}
	
	public void assembly(JavaGenerate jg, String templateRsc) throws IOException {
		// 首次将模板解析为(char)0
		ArrayDeque<String> codes = new ArrayDeque<String>();
		// 首次将模板解析为(char)1
		ArrayDeque<String> params = new ArrayDeque<String>();
		String template = new String(templateRsc);
		// 按指定模式在字符串查找
		String patternCode = parseSign + "<(.[^>]*)>";
		String patternParam = parseSign + "\\{(.[^\\}]*)\\}";
		// 创建 Pattern 对象
		Pattern r = Pattern.compile(patternCode);

		// 现在创建 matcher 对象
		Matcher m = r.matcher(template);
		while (m.find()) {
			codes.add(m.group(1));
			template = template.replace(m.group(0), String.valueOf((char) 0));
		}

		Pattern r2 = Pattern.compile(patternParam);
		m = r2.matcher(template);
		while (m.find()) {
			params.add(m.group(1));
			template = template.replace(m.group(0), String.valueOf((char) 1));
		}
		
		ByteArrayInputStream bis = new ByteArrayInputStream(template.getBytes());  
		BufferedReader br = null;
		try {
			InputStreamReader ru = new InputStreamReader(bis,charset);
			br = new BufferedReader(ru);
			String str = null;
	        while ((str = br.readLine()) != null) {
	    		// 现在创建 matcher 对象
	    		Matcher mp = Pattern.compile((char)0+"|"+(char)1).matcher(str);
	    		int start = 0;
	    		int textPrintNum = 0;
	    		while(mp.find()){
	    			byte b = mp.group().getBytes()[0];
	    			if(b == 0){//code
	    				String tmp = str.substring(start, mp.start());
	    				start = mp.start()+1;
	    				if(!"".equals(tmp.trim())){
	    					textPrintNum ++;
	    					jg.printTemplate(codingUtil.encoding(tmp));
	    				}
	    				jg.addCode(codes.pop());
	    			}else{//param
	    				String tmp = str.substring(start, mp.start());
	    				start = mp.start()+1;
	    				//if(!"".equals(tmp.trim())){
	    					textPrintNum ++;
	    					jg.printTemplate(codingUtil.encoding(tmp));
	    				//}
	    				jg.printParam(params.pop());
	    			}
	    		}
	    		if(start < str.length()){
	    			String tmp = str.substring(start, str.length());
	    			if(!"".equals(tmp.trim())){
    					textPrintNum ++;
    					jg.printTemplate(codingUtil.encoding(tmp));
    				}
	    		}
	    		if(start ==0 || textPrintNum != 0)
	    			jg.printlnTemplate("");//回车
	        }
		}finally {
			if(br != null)
				br.close();
		}
	}
	
	public List<Column> disposeColumn(ResultSet rs) throws SQLException{
		List<Column> cs = new ArrayList<Column>();
		while(rs.next()){
			Column c = new Column();
			c.setTable_column(rs.getString(TableColumn.COLUMN.getDesc()));
			c.setTable_dateType(rs.getString(TableColumn.DATA_TYPE.getDesc()));
			c.setColumn(columnNameSwitch.nameSwitch(rs.getString(TableColumn.COLUMN.getDesc())));
			c.setDateType(dataTypeSwitch.typeSwitch(rs.getString(TableColumn.DATA_TYPE.getDesc())));
			c.setColumnComment(rs.getString(TableColumn.COMMENT.getDesc()));
			cs.add(c);
		}
		return cs;
	}
	
	/*
	 * 
	 */
	private void parseXML(String configXML) throws DocumentException, ClassNotFoundException, SQLException, InstantiationException, IllegalAccessException, IOException {
		// 创建一个XML解析器
		SAXReader saxReader = new SAXReader();
		// 读取Document对象
		Document document = null;
		document = saxReader.read(TemplateParse.class.getResource(configXML));
		Element root = document.getRootElement();
		Element context = root.element("context");
		
		//获取DB源
		Element DB = context.element("jdbcConnection");
		conn = getDBConnect(DB.attributeValue("connectionURL"), DB.attributeValue("userId"), DB.attributeValue("password"), DB.attributeValue("driverClass"));
		
		//获取解析前缀
		Element prev = root.element("parseSign");
		if(prev != null && prev.attributeValue("value") != null && prev.attributeValue("value") != ""){
			parseSign = prev.attributeValue("value");
		}
		
		//获取文件生成的默认地址
		Element gl = context.element("generateLoacl");
		defaultLocal = gl.attributeValue("local");
		//获取模板列表
		Iterator<Element> templates = context.elementIterator("templateGenerator");
		while(templates.hasNext()){
			Element e = templates.next();
			TemplateObject to = new TemplateObject();
			to.setTargetSRC(e.attributeValue("targetSRC"));
			to.setLocal(e.attributeValue("local"));
			to.setType(e.attributeValue("type"));
			to.setTargetSuffix(e.attributeValue("targetSuffix"));
			to.setTemplate(getFileContent("/"+e.attributeValue("templateSRC").replaceAll("\\.", "/")+"/"+e.attributeValue("templateName")));
			temps.put(e.attributeValue("name"),to);
		}
		
		//获取table
		Iterator<Element> ts = context.elementIterator("table");
		while(ts.hasNext()){
			Element e = ts.next();
			TableObject to = new TableObject();
			to.setTableName(e.attributeValue("tableName"));
			to.setDomainObjectName(e.attributeValue("domainObjectName"));
			tempVariables.put(to.getTableName(), e.attributeValue("nameLike"));
			tables.add(to);
		}
		
		//注解类配置
		Element comment = context.element("commentGenerator");
		if(comment != null && comment.attributeValue("type") != null && comment.attributeValue("type") != ""){
			commentGenerator = (CommentGenerator) Class.forName(comment.attributeValue("type")).newInstance();
		}
		//获取列名解析类配置
		Element columnSwitch = context.element("columnSwitch");
		if(columnSwitch != null && columnSwitch.attributeValue("type") != null && columnSwitch.attributeValue("type") != ""){
			columnNameSwitch = (ColumnNameSwitch) Class.forName(columnSwitch.attributeValue("type")).newInstance();
		}else{
			columnNameSwitch = new DefaultColumnNameSwitch();
		}
		//获取模板变量
		Iterator<Element> tempVariable = context.elementIterator("tempVariable");
		while(tempVariable.hasNext()){
			Element e = tempVariable.next();
			tempVariables.put(e.attributeValue("key"), e.attributeValue("value"));
		}
	}
	
	private Connection getDBConnect(String url, String user, String password, String driverName) throws ClassNotFoundException, SQLException {
		// 通过得到字节码对象的方式加载静态代码块，从而注册驱动程序
		Class.forName(driverName);
		Connection conn = DriverManager.getConnection(url, user, password);
		return conn;
	}
	
	private String getFileContent(String uri) throws IOException{
		BufferedReader br = null;
		StringBuffer sb = new StringBuffer();
		try{
			URL url = TemplateParse.class.getResource(uri);
			if(url == null){
				throw new RuntimeException(uri+" 路径不正确！");
			}
			File f = new File(url.getFile());
			if(!f.exists()){
				return null;
			}
			FileInputStream fis = new FileInputStream(f);
			InputStreamReader ru = new InputStreamReader(fis,charset);
	        br = new BufferedReader(ru);
	        String str = null;
	        while ((str = br.readLine()) != null) {
	        	sb.append(str);
	        	sb.append("\n");
	        }
		}finally {
			if(br != null)
				br.close();
		}
		return sb.toString();
	}
	
	public File getFile(String uri) throws IOException{
		uri = pathAnalysis.PathAnalysis(uri);
		File f = new File(uri);
		if(!f.exists()){
			f.createNewFile();
		}
		return f;
	}
	
	public static void main(String[] args) throws Exception {
		new TemplateParse().parse("/gen/background/generator.xml");
		//System.out.println(new TemplateParse().getFileContent("/gen/background/resource/CouponCode.jsp"));
//		JavaGenerate jg = new JavaGenerate();
//		new TemplateParse().assembly(jg, "public class aaaa { @#<String name = \"测试\";> String code = \"\"@#{name}\" }");
//		System.out.println(jg.initContent());
//		//生成模板
//		File file = new TemplateParse().getFile("gen/background/resource/generator111.xml");
//		jg.execute(null, file, null);//param 扩展的map参数暂未配置传空
	}
}
